﻿using System;
using System.Collections.Generic;
using DimOS.Constants;
using DimOS.Internals;
using DimOS.Plugs;
using DimOS.VTS;
using Console = System.Console;
using UI = DimOS.DimSH.Interface;
using HAL = Cosmos.Hardware;
using CORE = Cosmos.Core;
using SH = DimOS.DimSH.Shell;
// ReSharper disable AccessToStaticMemberViaDerivedType
// ReSharper disable ForCanBeConvertedToForeach

namespace DimOS.DimSH
{
    public static class Terminal
    {
        private static readonly List<CharColor[]> ScrollUpBuffer = new List<CharColor[]>();
        private static readonly List<CharColor[]> ScrollDownBuffer = new List<CharColor[]>();
        private static readonly int LastRow = UI.Rows - 3;

        public static int CurrentScreen;

        public static byte ScrollStatus {
            get { 
                if (Settings.AdvancedTerminal) return VTS.Console.ScrollStatus;

                if (ScrollUpBuffer.Count == 0 && ScrollDownBuffer.Count != 0) return 1;
                if (ScrollUpBuffer.Count != 0 && ScrollDownBuffer.Count == 0) return 0;
                if (ScrollUpBuffer.Count != 0 && ScrollDownBuffer.Count != 0) return 2;
                if (ScrollUpBuffer.Count == 0 && ScrollDownBuffer.Count == 0) return 3;
                return 4;
            } }

        public static void WriteLine(bool drawClock = true,
            bool scrollEnd = false, bool changePos = true, bool mainScreenOnly = false)
        { Print("", true, drawClock, true, scrollEnd, changePos, mainScreenOnly); }

        public static void WriteLine(string text, bool drawClock = true,
            bool autoFormatting = true, bool scrollEnd = false, bool changePos = true, bool mainScreenOnly = false)
        { Print(text, true, drawClock, autoFormatting, scrollEnd, changePos, mainScreenOnly); }

        //Writes a specific text to the output without addition of new line
        public static void Write(string text, bool drawClock = true,
            bool autoFormatting = true, bool scrollEnd = false, bool changePos = true, bool mainScreenOnly = false)
        { Print(text, false, drawClock, autoFormatting, scrollEnd, changePos, mainScreenOnly); }

        public static void Write(char character, bool drawClock = true,
            bool autoFormatting = true, bool scrollEnd = false, bool changePos = true, bool mainScreenOnly = false)
        { Print(character.ToString(), false, drawClock, autoFormatting, scrollEnd, changePos, mainScreenOnly); }

        public static string[] Format(string text, int limit)
        {
            var fitText = "";
            var fitTextList = new List<string>();
            var lines = text.Split(new[] { Tokens.NewLine }, StringSplitOptions.None);

            for (int line = 0; line < lines.Length; line++)
            {
                var textCopy = lines[line];
                var j = -1;
                for (int i = 0; i < lines[line].Length; i++)
                {
                    j++;
                    if (j == limit && i != lines[line].Length)
                    {
                        fitTextList.Add(lines[line].Substring(i - j, j));
                        textCopy = textCopy.Remove(0, j);
                        j = 0;
                    }
                }
                fitTextList.Add(textCopy + Tokens.NewLine);
            }

            for (int i = 0; i < fitTextList.Count; i++)
            {
                if (fitTextList[i][0] == Tokens.Space && i != 0) fitTextList[i] = fitTextList[i].Remove(0, 1);
                if (fitTextList[i].IndexOf(Tokens.NewLine) == -1)
                {
                    if (fitTextList[i].Length < limit && i != fitTextList.Count - 1)
                    {
                        var difference = limit - fitTextList[i].Length;
                        fitTextList[i] += fitTextList[i + 1].Substring(0, difference);
                        fitTextList[i + 1] = fitTextList[i + 1].Remove(0, difference);
                    }
                    fitText += fitTextList[i] + Tokens.NewLine;
                }
                else fitText += fitTextList[i];
            }
            fitText = fitText.Remove(fitText.Length - 1, 1);
            return fitText.Split(new [] { Tokens.NewLine }, StringSplitOptions.None);
        }

        public static char Read()
        {
            UI.Y = UI.Rows - 1;
            HAL.Keyboard.KeyMapping map; GC.Heap.Monitor();
            do
            {          
                var until = HAL.RTC.Second + 1;
                if (until > 59) until -= 60;
                ShowCursor();
                while (!HAL.Global.Keyboard.GetMapping(out map))
                {
                    if (until != HAL.RTC.Second) continue;

                    HideCursor();
                    UI.DrawClock(false);
                    ShowCursor();

                    until = HAL.RTC.Second + 1;
                    if (until > 59) until -= 60;
                }
            } while (map == null); HideCursor();
            //if (UI.X >= UI.OutputColumns) { ScrollRight(); }
            Write(map.Value, false); GC.Heap.Reclaim();
            return map.Value;
        }

        public static void Print(List<CharColor> list, int start, bool changePos = true, bool mainScreenOnly = false)
        { for (int i = start; i < list.Count; i++) Print(list[i], i != list.Count - 1 || changePos, mainScreenOnly); }

        public static void Print(CharColor charColor, bool changePos = true, bool mainScreenOnly = false)
        {
            UI.Foreground = (ConsoleColor)(charColor.Color & 0xF);
            UI.Background = (ConsoleColor)((charColor.Color & 0xF0) >> 4);
            Print(charColor.Character.ToString(), false, false, false, false, changePos, mainScreenOnly);
        }

        private static void Print(string text, bool newLine, bool drawClock = true,
            bool autoFormatting = true, bool scrollEnd = false, bool changePos = true, bool mainScreenOnly = false)
        {
            if (!Settings.AdvancedTerminal && scrollEnd) ScrollEnd();
            if (!drawClock)
            {
                if (newLine) { if (Settings.AdvancedTerminal) VTS.Console.WriteLine(text, changePos, scrollEnd, mainScreenOnly);
                    else { if (changePos) Console.WriteLine(text); else { Console.Write(text.Remove(text.Length - 1));
                    Screen.SetCharacter(UI.X, UI.Y, text[text.Length - 1], UI.Foreground, UI.Background); Console.WriteLine(); } } }
                else { if (Settings.AdvancedTerminal) VTS.Console.Write(text, changePos, scrollEnd, mainScreenOnly);
                    else { if (changePos) Console.Write(text); else { Console.Write(text.Remove(text.Length - 1));
                    Screen.SetCharacter(UI.X, UI.Y, text[text.Length - 1], UI.Foreground, UI.Background); } } }
            }
            else
            {
                string[] finalText = autoFormatting
                    ? Format(text, UI.OutputColumns)
                    : text.Split(new[] { Tokens.NewLine }, StringSplitOptions.None);

                for (int i = 0; i < finalText.Length; i++)
                {
                    UI.Y = UI.Rows - 3;
                    if (!newLine && i == finalText.Length - 1)
                    {
                        if (Settings.AdvancedTerminal) VTS.Console.Write(finalText[i], changePos, scrollEnd, mainScreenOnly);
                        else { if (changePos) Console.Write(finalText[i]);
                            else { Console.Write(finalText[i].Remove(finalText[i].Length - 1));
                            Screen.SetCharacter(UI.X, UI.Y, finalText[i][finalText[i].Length - 1],
                            UI.Foreground, UI.Background); } }
                    }
                    else
                    {      
                        if (Settings.AdvancedTerminal) VTS.Console.WriteLine(finalText[i], changePos, scrollEnd, mainScreenOnly);
                        else { UI.ClearSections(); if (changePos) { Console.Write(finalText[i]); Scroll(); }
                            else { Console.Write(finalText[i].Remove(finalText[i].Length - 1));
                            Screen.SetCharacter(UI.X, UI.Y, finalText[i][finalText[i].Length - 1],
                            UI.Foreground, UI.Background); } UI.DrawSections(); }
                        
                    }
                }
            }
        }

        public static void Wait(int seconds, Action action = null)
        {      
            //int current = HAL.RTC.Second;
            var until = HAL.RTC.Second + seconds;
            if (until > 59) until -= 60;
            // ReSharper disable once LoopVariableIsNeverChangedInsideLoop
            while (until != HAL.RTC.Second) if (action != null) action.Invoke();
        }

        public static void ShowCursor()
        {
            if (Settings.AdvancedTerminal) VTS.Console.ShowCursor();
            else Screen.ShowCursor();
        }

        public static void HideCursor()
        {
            if (Settings.AdvancedTerminal) VTS.Console.HideCursor();
            else Screen.HideCursor();
        }

        public static void ScrollUp()
        {
            if (Settings.AdvancedTerminal) VTS.Console.CurrentScreen.ScrollUp(true);
            else
            {          
                var count = ScrollUpBuffer.Count;             
                if (count != 0)
                {
                    UI.ClearSections();
                    var row = new CharColor[UI.OutputColumns];
                    for (int i = 0; i < UI.OutputColumns; i++) row[i] =
                        new CharColor(Screen.GetCharacter(i, LastRow),
                        Screen.GetColor(i, LastRow));
                    ScrollDownBuffer.Add(row);

                    Screen.ScrollBackward();

                    for (int i = 0; i < UI.OutputColumns; i++)
                    {
                        var temp = ScrollUpBuffer[count - 1][i];
                        Screen.SetCharacter(i, 0, temp.Character, temp.Color);
                    }

                    ScrollUpBuffer.RemoveAt(count - 1);
                    UI.DrawSections();
                }            
            }
            UI.DrawScrollIndicator();
        }

        public static void ScrollDown()
        {
            if (Settings.AdvancedTerminal) VTS.Console.CurrentScreen.ScrollDown(true);
            else
            {
                var count = ScrollDownBuffer.Count;
                if (count != 0)
                {
                    UI.ClearSections();
                    var row = new CharColor[UI.OutputColumns];
                    for (int i = 0; i < UI.OutputColumns; i++) row[i] = 
                        new CharColor(Screen.GetCharacter(i, 0), Screen.GetColor(i, 0));
                    ScrollUpBuffer.Add(row);

                    Screen.ScrollForward();

                    for (int i = 0; i < UI.OutputColumns; i++)
                    {
                        var temp = ScrollDownBuffer[count - 1][i];
                        Screen.SetCharacter(i, LastRow, temp.Character, temp.Color);
                    }
                    ScrollDownBuffer.RemoveAt(count - 1);
                    UI.DrawSections();
                }
            }
            UI.DrawScrollIndicator();
        }

        public static void Scroll(bool drawScrollIndicator = true)
        {
            if (UI.Rows - 2 - UI.RowOffset > 0)
            { var row = new CharColor[UI.OutputColumns]; for (int i = 0;
              i < UI.OutputColumns; i++) row[i] = new CharColor(Screen
              .GetCharacter(i, 0), Screen.GetColor(i, 0)); ScrollUpBuffer.Add
              (row); Screen.ScrollForward(); } else Screen.ScrollForward(1);
            if (drawScrollIndicator) UI.DrawScrollIndicator(); UI.X = 0;
        }

        public static void FinishInput()
        {   
            if (UI.RowOffset != 0)
            {
                if (!Settings.AdvancedTerminal) UI.ClearSections();
                for (int i = 0; i < UI.RowOffset; i++)
                {                
                    Screen.ScrollBackward();
                    for (int j = 0; j < UI.OutputColumns; j++)
                    { var temp = ScrollUpBuffer[ScrollUpBuffer.Count - 1][j];
                      Screen.SetCharacter(j, 0, temp.Character, temp.Color); }
                    ScrollUpBuffer.RemoveAt(ScrollUpBuffer.Count - 1);
                } UI.RowOffset = 0; UI.DrawPath(true);
                if (!Settings.AdvancedTerminal) UI.DrawSections();
            }

            UI.Foreground = Colors.SectionOutputForeground;
            UI.Background = Colors.SectionOutputBackground;

            SH.InputIndicatorColor = Colors.IndicatorInputDefault;
            UI.DrawInputIndicator(SH.InputIndicatorColor);
            //?
        }

        public static void ScrollEnd()
        { if (Settings.AdvancedTerminal) VTS.Console.CurrentScreen.ScrollEnd(true);
          else for (int i = ScrollDownBuffer.Count; i > 0; i--) ScrollDown(); }

        public static void ScrollLeft(ref List<CharColor> left, ref List<CharColor> center,
            ref List<CharColor> right, int limit, bool insert = false, bool truncate = false)
        {
            if (left.Count != 0 && insert)
            { List.Insert(ref center, 0, left[left.Count - 1]); left.RemoveAt(left.Count - 1); }
            if (!truncate || center.Count == limit)
            { List.Insert(ref right, 0, center[center.Count - 1]); center.RemoveAt(center.Count - 1); }
        }

        public static void ScrollRight(ref List<CharColor> left, ref List<CharColor> center,
            ref List<CharColor> right, int limit, bool insert = false, bool truncate = false)
        {
            if (right.Count != 0 && insert) { center.Add(right[0]); right.RemoveAt(0); }
            if (!truncate || center.Count == limit) { left.Add(center[0]); center.RemoveAt(0); }
        }   

        public static void SwitchScreen(Screen screen,
            ConsoleColor foreground = Colors.SectionInputForeground,
            ConsoleColor background = Colors.SectionInputBackground)
        {
            if (Settings.AdvancedTerminal)
            {
                VTS.Console.CurrentScreen = screen;
                VTS.Console.CurrentScreen.Display(foreground, background);

                for (int i = 0; i < Screens.List.Count; i++)
                    if (Screens.List[i] == screen) CurrentScreen = i;
                UI.DrawScreens(); UI.DrawScrollIndicator();
            }          
        }

        public static void SetColor(int x, int y, ConsoleColor foreground, ConsoleColor background)
        {
            if (Settings.AdvancedTerminal) VTS.Console.SetColor(x, y, foreground, background);
            else
            {
                Screen.SetForeground(x, y, foreground);
                Screen.SetBackground(x, y, background);
            }
        }

        public static ConsoleColor GetForeground(int x, int y)
        {
            if (Settings.AdvancedTerminal) return VTS.Console.GetForeground(x, y);
            return (ConsoleColor)(Screen.GetColor(x, y) & 0xF);
        }

        public static ConsoleColor GetBackground(int x, int y)
        {
            if (Settings.AdvancedTerminal) return VTS.Console.GetBackground(x, y);
            return (ConsoleColor)((Screen.GetColor(x, y) & 0xF0) >> 4);
        }

        public static char GetCharacter(int x, int y)
        {
            if (Settings.AdvancedTerminal) return VTS.Console.GetCharacter(x, y);
            return Screen.GetCharacter(x, y);
        }

        public static void SetCursorSize(int size)
        {
            if (Settings.AdvancedTerminal) VTS.Console.SetCursorSize(size);
            else Screen.SetCursorSize(size);
        }
    }
}
